home *** CD-ROM | disk | FTP | other *** search
/ Mac Power 1997 January / macpower199701.bin / AMUG / Programming_10 / WASTE 1.3a1.sit / WASTE 1.3a1 Distribution / WASTE 1.3a1 / WELineLayout.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-08-23  |  14.6 KB  |  500 lines  |  [TEXT/CWIE]

  1. /*
  2.  *    WELineLayout.c
  3.  *
  4.  *    WASTE PROJECT
  5.  *  Line Layout, Getting and Setting Variables, etc.
  6.  *
  7.  *  Copyright (c) 1993-1996 Marco Piovanelli
  8.  *    All Rights Reserved
  9.  *
  10.  *  C port by Dan Crevier
  11.  *
  12.  */
  13.  
  14.  
  15. #include "WASTEIntf.h"
  16.  
  17. INLINE pascal void _WERemoveLine(SInt32 lineIndex, WEPtr pWE)
  18. {
  19.     // remove the line
  20.     _WESplice((Handle) pWE->hLines, nil, - sizeof(LineRec), lineIndex * sizeof(LineRec));
  21.  
  22.     // decrement line count
  23.     pWE->nLines--;
  24. }
  25.  
  26. pascal OSErr _WEInsertLine(SInt32 lineIndex, const LineRec *pLine, WEPtr pWE)
  27. {
  28.     // insert the specified element in the line array
  29.     OSErr err;
  30.  
  31.     // do the insertion
  32.     if ((err = _WESplice((Handle) pWE->hLines, pLine, sizeof(LineRec), lineIndex * sizeof(LineRec))) != noErr)
  33.         return err;
  34.  
  35.     // increment line count
  36.     pWE->nLines++;
  37.     return noErr;
  38. }
  39.  
  40. pascal void _WEBumpOrigin(SInt32 lineIndex, SInt32 deltaOrigin, WEPtr pWE)
  41. {
  42.     LineRec *pLine = *pWE->hLines + lineIndex;
  43.     SInt32 nLines = pWE->nLines;
  44.  
  45.     // loop through the line run array adjusting the lineOrigin fields
  46.     for ( ; lineIndex <= nLines; lineIndex++ )
  47.     {
  48.         pLine->lineOrigin += deltaOrigin;
  49.         pLine++;
  50.     }
  51. }
  52.  
  53. pascal SInt32 _WEFindLineBreak(SInt32 lineStart, WEHandle hWE)
  54. {
  55.     // Find where to break the line beginning at lineStart
  56.     // the current graphics port must be already set up correctly
  57.  
  58.     WEPtr pWE = *hWE;    // assume WE record is already locked
  59.     Ptr pText;
  60.     SInt32 offset, breakOffset;
  61.     SInt32 textLength;
  62.     SInt32 remainingLength;
  63.     SInt32 segmentStart, segmentEnd;
  64.     SInt32 runIndex;
  65.     WERunInfo runInfo;
  66.     Fixed pixelWidth;
  67.     ScriptCode script, previousScript;
  68.     Boolean isBreak = false;
  69.  
  70.     offset = lineStart;
  71.     pText = *pWE->hText + offset;
  72.     remainingLength = pWE->textLength - offset;
  73.  
  74.     // find the style run index corresponding to the first segment on this line
  75.     runIndex = WEOffsetToRun(offset, hWE);
  76.  
  77.     // initialize pixelWidth to the width of the destination rectangle, as a Fixed quantity
  78.     pixelWidth = BSL((pWE->destRect.right - pWE->destRect.left), 16);
  79.  
  80.     // STYLE SEGMENT LOOP
  81.     do
  82.     {
  83.  
  84.         // get style run information for the current style run
  85.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  86.         runIndex++;
  87.  
  88.         // set text attributes in the graphics port
  89.         TextFont(runInfo.runAttrs.runStyle.tsFont);
  90.         TextFace(runInfo.runAttrs.runStyle.tsFace);
  91.         TextSize(runInfo.runAttrs.runStyle.tsSize);
  92.  
  93.         // if we're handling multiscript text, keep track of script boundaries
  94.         if (BTST(pWE->flags, weFNonRoman))
  95.         {
  96.             // what is the script for this segment?
  97.             script = FontToScript(runInfo.runAttrs.runStyle.tsFont);
  98.  
  99.             // have we crossed a script run boundary in the middle of a line?
  100.             if ((runInfo.runStart > offset) && (script != previousScript))
  101.             {
  102.                 // leave behind the all previous segments on this line
  103.                 offset = runInfo.runStart;
  104.                 pText = *pWE->hText + offset;
  105.                 remainingLength = pWE->textLength - offset;
  106.             }
  107.             previousScript = script;
  108.         } // if non-Roman
  109.  
  110.         // we'll pass textLength as the second parameter to the line break hook
  111.         // although this parameter is declared as a long, StyledLineBreak uses only
  112.         // the low word, so make sure it doesn't trespass the 32,767 byte threshold!
  113.         textLength = _WEPinInRange(remainingLength, 0, SHRT_MAX);
  114.  
  115.         // calculate segmentStart and segmentEnd relative to offset
  116.         segmentStart = _WEPinInRange(runInfo.runStart - offset, 0, textLength);
  117.         segmentEnd = _WEPinInRange(runInfo.runEnd - offset, 0, textLength);
  118.  
  119.         // set breakOffset to a non-zero value for the first script run on the line,
  120.         // set it to zero for all subsequent script runs
  121.         breakOffset = (offset == lineStart);
  122.  
  123. #if WASTE_OBJECTS
  124.         if (runInfo.runAttrs.runStyle.tsObject != nil)
  125.         {
  126.             // EMBEDDED OBJECT
  127.             // subtract object width from pixelWidth
  128.             pixelWidth -= BSL((*runInfo.runAttrs.runStyle.tsObject)->objectSize.h, 16);
  129.  
  130.             // stop looping if pixelWidth has gone negative
  131.             isBreak = (pixelWidth < 0);
  132.             breakOffset = isBreak ? segmentStart : segmentEnd;
  133.         }
  134.         else
  135. #endif
  136.         {
  137.             // REGULAR TEXT
  138.             isBreak = (CallWELineBreakProc(pText, textLength, segmentStart, segmentEnd,
  139.                 &pixelWidth, &breakOffset, hWE, pWE->lineBreakHook) != smBreakOverflow);
  140.         }
  141.  
  142.         // break the line anyway when we reach the end of the text
  143.         if (segmentEnd >= remainingLength)
  144.             isBreak = true;
  145.  
  146.     } while (!isBreak);
  147.  
  148.     // return the offset from lineStart to the break point
  149.     return (offset - lineStart) + breakOffset;
  150. }
  151.  
  152. pascal void _WECalcHeights(SInt32 rangeStart, SInt32 rangeEnd, SInt16 *lineAscent, SInt16 *lineDescent,
  153.         WEHandle hWE)
  154. {
  155.     // Find the maximum ascent and descent values between rangeStart and rangeEnd
  156.     // the WE record must be already locked
  157.     // the current graphics port must be already set up correctly
  158.  
  159.     SInt32 runIndex;
  160.     WERunInfo runInfo;
  161.     SInt16 runAscent, runDescent;
  162.  
  163.     *lineAscent = 1;
  164.     *lineDescent = 0;
  165.  
  166.     // find the style run index corresponding to the first segment on this line
  167.     runIndex = WEOffsetToRun(rangeStart, hWE);
  168.  
  169.     // STYLE SEGMENT LOOP
  170.     do
  171.     {
  172.         // get style run information for the current style run
  173.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  174.         runIndex++;
  175.  
  176.         // calculate ascent and descent (actually, descent + leading) values for this style run
  177.  
  178. #if WASTE_OBJECTS
  179.         if (runInfo.runAttrs.runStyle.tsObject != nil)
  180.         {
  181.             // EMBEDDED OBJECT
  182.             runAscent = (*runInfo.runAttrs.runStyle.tsObject)->objectSize.v;
  183.             runDescent = 0;
  184.         }
  185.         else
  186. #endif
  187.         {
  188.             // REGULAR TEXT
  189.             runAscent = runInfo.runAttrs.runAscent;
  190.             runDescent = runInfo.runAttrs.runHeight - runAscent;
  191.         }
  192.  
  193.         // save the maximum values in lineAscent and lineDescent
  194.         if (runAscent > *lineAscent)
  195.             *lineAscent = runAscent;
  196.  
  197.         if (runDescent > *lineDescent)
  198.             *lineDescent = runDescent;
  199.  
  200.         // keep looping until we reach rangeEnd
  201.     } while (runInfo.runEnd < rangeEnd);
  202. }
  203.  
  204. pascal OSErr _WERecalBreaks(SInt32 *startLine, SInt32 *endLine, WEHandle hWE)
  205. {
  206.     // Recalculates line breaks, line heights and ascents for all the text or for a portion of it.
  207.     // On entry, startLine and endLine define a range of lines to recalculate.
  208.     // On exit, startLine to endLine defines the range of lines actually recalculated
  209.     // the WE record must already be locked
  210.  
  211.     WEPtr pWE = *hWE;
  212.     LinePtr pLine;
  213.     LineRec lineInfo, oldLineInfo;
  214.     SInt32 lineIndex;
  215.     SInt32 recalThreshold;
  216.     SInt32 lineOffset;
  217.     SInt16 lineAscent, lineDescent;
  218.     SInt32 textHeight;
  219.     Boolean saveTextLock;
  220.     QDEnvironment saveEnvironment;
  221.     OSErr err = noErr;
  222.  
  223.     // lock the text
  224.     saveTextLock = _WESetHandleLock(pWE->hText, true);
  225.  
  226.     // find the character offset that must be necessarily reached before we can
  227.     // even consider the possibility of stopping the recalculation process
  228.     // this offset, recalThreshold, is the last character on endLine _before_ recalculation
  229.     lineIndex = _WEPinInRange(*endLine, 0, pWE->nLines - 1);
  230.     recalThreshold = (*pWE->hLines)[lineIndex + 1].lineStart;
  231.  
  232.     // we start recalculating line breaks from the line actually _preceding_ startLine,
  233.     // since editing startLine may cause part of its text to fit on the preceding line
  234.     lineIndex = _WEPinInRange(*startLine - 1, 0, pWE->nLines - 1);
  235.  
  236.     // find where in the text recalculation should begin
  237.     lineInfo = (*pWE->hLines)[lineIndex];
  238.  
  239.     // save the Quickdraw environment
  240.     _WESaveQDEnvironment(pWE->port, false, &saveEnvironment);
  241.  
  242.     // MAIN LINE BREAKING LOOP
  243.     do
  244.     {
  245.         // find where to break the current line
  246.         lineOffset = _WEFindLineBreak(lineInfo.lineStart, hWE);
  247.  
  248.         // make sure we advance at least by one character (unless we reached the end of text)
  249.         if ((lineOffset <= 0) && (lineInfo.lineStart < pWE->textLength))
  250.             lineOffset = 1;
  251.  
  252.         // calculate ascent and descent values for this line
  253.         _WECalcHeights(lineInfo.lineStart, lineInfo.lineStart + lineOffset, &lineAscent, &lineDescent, hWE);
  254.  
  255.         // save the maximum line ascent for this line in the line array
  256.         pLine = *pWE->hLines + lineIndex;
  257.         pLine->lineAscent = lineAscent;
  258.  
  259.         // increment counters (go to the next line array entry)
  260.         lineIndex++;
  261.         lineInfo.lineStart += lineOffset;
  262.         lineInfo.lineOrigin += (lineAscent + lineDescent);
  263.         pLine++;
  264.  
  265.         // compare the newly calculated line start with the old value
  266.         // if the new line start comes before the old line start, insert a new element
  267.         oldLineInfo = *pLine;
  268.         if ((lineIndex > pWE->nLines) || (lineInfo.lineStart < oldLineInfo.lineStart))
  269.         {
  270.             if ((err = _WEInsertLine(lineIndex, &lineInfo, pWE)) != noErr)
  271.                 goto cleanup;
  272.         }
  273.         else
  274.         {
  275.             // overwrite the old element
  276.             pLine->lineStart = lineInfo.lineStart;
  277.             pLine->lineOrigin = lineInfo.lineOrigin;
  278.  
  279.             // remove all further elements which have a lineStart field
  280.             // less than or equal to the current one
  281.             while((lineIndex < pWE->nLines) && (lineInfo.lineStart >= (pLine + 1)->lineStart))
  282.                 _WERemoveLine(lineIndex + 1, pWE);
  283.  
  284.             // if the new line start is the same as the old one...
  285.             if (lineInfo.lineStart == oldLineInfo.lineStart)
  286.             {
  287.                 // ...and recalThreshold has been reached, we can stop recalculating line breaks
  288.                 if (lineInfo.lineStart >= recalThreshold)
  289.                 {
  290.                     // although line breaks need not be changed from lineIndex on,
  291.                     // the lineOrigin fields may need to be changed
  292.                     if (lineInfo.lineOrigin != oldLineInfo.lineOrigin)
  293.                         _WEBumpOrigin(lineIndex + 1, lineInfo.lineOrigin - oldLineInfo.lineOrigin, pWE);
  294.  
  295.                     // exit from the line breaking loop
  296.                     goto cleanup;
  297.                 }
  298.             }
  299.             else
  300.             {
  301.                 // otherwise, the new line start comes after the old line start...
  302.                 // if the current line is the one preceding startLine, warn our caller about this
  303.                 if ((lineIndex > 0) && (lineIndex == *startLine))
  304.                     *startLine = lineIndex - 1;
  305.             }
  306.         }
  307.     } while(lineInfo.lineStart < pWE->textLength);
  308.  
  309. cleanup:
  310.     // calculate total text height
  311.     textHeight = WEGetHeight(0, pWE->nLines, hWE);
  312.  
  313.     // quirk: if the last character in the text is a carriage return, the caret appears
  314.     // below the last line, so in this case we need to add the extra height to textHeight
  315.     if (WEGetChar(pWE->textLength - 1, hWE) == kEOL)
  316.         textHeight += WEGetHeight(pWE->nLines - 1, pWE->nLines, hWE);
  317.  
  318.     // if total text height has changed, remember to call the scroll callback later
  319.     if (textHeight != pWE->destRect.bottom - pWE->destRect.top)
  320.         BSET(pWE->flags, weFDestRectChanged);
  321.  
  322.     // set destRect.bottom to destRect.top + total text height
  323.     pWE->destRect.bottom = pWE->destRect.top + textHeight;
  324.  
  325.     // return through endLine the index of the last line affected by recalculation
  326.     *endLine = lineIndex - 1;
  327.  
  328.     // make sure startLine isn't greater than endLine
  329.     if (*startLine > *endLine)
  330.         *startLine = *endLine;
  331.  
  332.     // unlock the text
  333.     _WESetHandleLock(pWE->hText, saveTextLock);
  334.  
  335.     // restore the Quickdraw environment
  336.     _WERestoreQDEnvironment(&saveEnvironment);
  337.  
  338.     // return result code
  339.     return err;
  340. }
  341.  
  342. static Boolean SLCalcSlop(LinePtr pLine, const WERunAttributes *pAttrs,
  343.         Ptr pSegment, SInt32 segmentStart, SInt32 segmentLength,
  344.         JustStyleCode styleRunPosition, WEHandle hWE, void *callbackData)
  345. {
  346.     struct SLCalcSlopData *cd = (struct SLCalcSlopData *) callbackData;
  347.     SInt16 segmentWidth;
  348.     Fixed segmentProportion;
  349.     Boolean isEndOfLine;
  350.  
  351.     // see if this text segment ends with a carriage return, or if we've reached the
  352.     // end of the text (in which case we don't want any justification to take place)
  353.     isEndOfLine = (segmentStart + segmentLength >= (*hWE)->textLength) ||
  354.                   (pSegment [ segmentLength - 1 ] == kEOL);
  355.  
  356.     // if this is the first segment on the line, reset line totals
  357.     if (IS_LEFTMOST_RUN(styleRunPosition))
  358.     {
  359.         cd->totalSlop = cd->lineWidth;
  360.         cd->totalProportion = 0;
  361.     }
  362.  
  363. #if WASTE_OBJECTS
  364.     if (pAttrs->runStyle.tsObject != nil)
  365.     {
  366.         // EMBEDDED OBJECT
  367.         // segment width is just object width; no extra space can be applied for justification
  368.         segmentWidth = (*pAttrs->runStyle.tsObject)->objectSize.h;
  369.         segmentProportion = 0;
  370.     }
  371.     else
  372. #endif
  373.     {
  374.         // REGULAR TEXT
  375.  
  376.         // if this is the last segment on the line, strip trailing spaces
  377.         if (IS_RIGHTMOST_RUN(styleRunPosition))
  378.         {
  379.             segmentLength = VisibleLength(pSegment, segmentLength);
  380.         }
  381.  
  382.         // measure this segment
  383.         segmentWidth = TextWidth(pSegment, 0, segmentLength);
  384.  
  385.         // calculate the proportion of extra space to apply to this text segment
  386.         segmentProportion = PortionLine(pSegment, segmentLength,
  387.                 styleRunPosition, kOneToOneScaling, kOneToOneScaling);
  388.     }
  389.  
  390.     // keep track of line totals
  391.     cd->totalSlop -= segmentWidth;
  392.     cd->totalProportion += segmentProportion;
  393.  
  394.     // if this is the last segment on the line, save values in the line array
  395.     if (IS_RIGHTMOST_RUN(styleRunPosition))
  396.     {
  397.         // make sure slop is non-negative
  398.         if (cd->totalSlop < 0)
  399.         {
  400.             cd->totalSlop = 0;
  401.         }
  402.         pLine->lineSlop = cd->totalSlop;
  403.         pLine->lineJustAmount = isEndOfLine ? 0 : FixDiv(BSL(cd->totalSlop, 16), cd->totalProportion);
  404.     }
  405.     return false;    // keep looping
  406. }
  407.  
  408. pascal void _WERecalSlops(SInt32 firstLine, SInt32 lastLine, WEHandle hWE)
  409. {
  410.     // Calculates the lineSlop and lineJustAmount fields
  411.     // of the line array for the specified lines
  412.  
  413.     WEPtr pWE = *hWE;
  414.     struct SLCalcSlopData cd;
  415.  
  416.     // we only need to bother if the user isn't using left justification
  417.     if (pWE->alignment == weFlushLeft)
  418.         return;
  419.  
  420.     // calculate slop and normalized slop proportion for all lines
  421.     cd.lineWidth = pWE->destRect.right - pWE->destRect.left;
  422.     _WESegmentLoop(firstLine, lastLine, SLCalcSlop, &cd, hWE);
  423. }
  424.  
  425. pascal void WESetAlignment(WEAlignment alignment, WEHandle hWE)
  426. {
  427.     WEPtr pWE;
  428.     WEAlignment oldAlignment;
  429.     Boolean saveWELock;
  430.  
  431.     // lock the WE record
  432.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  433.     pWE = *hWE;
  434.     oldAlignment = pWE->alignment;
  435.  
  436.     if ((alignment >= weFlushLeft) && (alignment <= weJustify) && (alignment != oldAlignment))
  437.     {
  438.         // hide the caret if it's showing
  439.         if (BTST(pWE->flags, weFCaretVisible))
  440.         {
  441.             _WEBlinkCaret(hWE);
  442.         }
  443.  
  444.         // change the alignment
  445.         pWE->alignment = alignment;
  446.  
  447.         if (! BTST(pWE->features, weFInhibitRecal))
  448.         {
  449.             // if the text was left-aligned, then we haven't been bothering till now,
  450.             // so we have to recalc the whole document
  451.             if (oldAlignment == weFlushLeft)
  452.             {
  453.                 _WERecalSlops(0, pWE->nLines - 1, hWE);
  454.             }
  455.  
  456.             if (! BTST(pWE->features, weFInhibitRedraw))
  457.             {
  458.                 // redraw the view rectangle
  459.                 WEUpdate(nil, hWE);
  460.             }
  461.         }
  462.     }
  463.  
  464.     // unlock the WE record
  465.     _WESetHandleLock((Handle) hWE, saveWELock);
  466. }
  467.  
  468. pascal void WESetDirection(WEDirection direction, WEHandle hWE)
  469. {
  470.     WEPtr pWE;
  471.     WEDirection oldDirection;
  472.     Boolean saveWELock;
  473.  
  474.     // lock the WE record
  475.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  476.     pWE = *hWE;
  477.     oldDirection = pWE->direction;
  478.  
  479.     if ((direction >= weDirRightToLeft) && (direction <= weDirDefault) && (direction != oldDirection))
  480.     {
  481.         // hide the caret if it's showing
  482.         if (BTST(pWE->flags, weFCaretVisible))
  483.         {
  484.             _WEBlinkCaret(hWE);
  485.         }
  486.  
  487.         // change the direction
  488.         pWE->direction = direction;
  489.  
  490.         if (! (pWE->features & ((1L << weFInhibitRecal) | (1L << weFInhibitRedraw))))
  491.         {
  492.             // redraw the view rectangle
  493.             WEUpdate(nil, hWE);
  494.         }
  495.     }
  496.  
  497.     // unlock the WE record
  498.     _WESetHandleLock((Handle) hWE, saveWELock);
  499. }
  500.